#ifndef INDEXADOHASHEXTENSIBLE_H_
#define INDEXADOHASHEXTENSIBLE_H_

#include <vector>

#include "Indice.h"
#include "Almacenamiento.h"
#include "EstrategiaHash.h"

class Cuba;
class Clave;
class Componente; //Util para TipoSerializacion


namespace Toolkit
{

class IndexadoHashExtensible : public Indice
{

private:
	/**
	 * Estrategia que encapsula el algoritmo de Hash.
	 */
	EstrategiaHash *estrategiaHash;

	Almacenamiento *almacenamientoCubas;
	vector<int> directorio;
	//Almacenamiento almacenamientoDirectorio; // encargado de inicializar el hash y de persistirlo cuando se cierra el hash.

	/**
	 * El atributo profundidad indica la cantidad de bits utilizados por el almacenamiento
	 * directorio para direccionar las Cubas.
	 */
	unsigned int profundidad;

public:
	IndexadoHashExtensible(const unsigned long int tamanoComponente,
						   TipoSerializacion &tSerializacion);
	virtual ~IndexadoHashExtensible();

	virtual bool eliminar(const Clave &clave, unsigned long int &posicionBloque);

	virtual void insertar(const Clave & clave, const unsigned long int posicionBloque);

	virtual bool buscar(const Clave &clave, unsigned long int &posicionBloque) const;

private:
	/**
	 * Dada una clave devuelve el numero del registro donde se debe encontrar. Para esto utiliza
	 * los ultimos bits del resultado de la funcionHash.
	 */
	unsigned long int crearDireccion(const Clave &clave) const;

	/**
	 * Duplica el tamano del directorio.
	 */
	void expandir();

	/**
	 * En el caso de que el directorio pueda contraerse, se reduce el tamaño del vector en un factor
	 * de 2 y devuelve true. En caso contrario devuelve false.
	 */
	bool contraer();

	/*
	 * Dada una clave, devuelve la cuba donde deberia estar la clave buscada.
	 */
	Cuba buscarCuba(const Clave &clave) const;

	/*
	 * Dada una direccion en el directorio, devuelve la cuba correspondiente.
	 */
	Cuba buscarCuba(const unsigned long int direccionDirectorio) const;


	/**
	 * Dada una cuba, la funcion devuelve la posicion del companero de esta a traves del argumento
	 * posicion. En caso de encontrar companero, devuelve true, de lo contrario false.
	 */
	bool buscarCompaneroCuba(const Cuba &cuba, unsigned long int &posicionCompanero) const;

	/**
	 * Dada una cuba, devuelve por argumento el rango de posiciones que debe ocupar en el
	 * almacenamientoDirectorio, siendo COMIENZO la primer posicion direccionada a esta cuba,
	 * y FIN la primer direccion consecutiva que NO esta direccionada a la misma.
	 */
	void calcularRangoDirecciones(const Cuba &cuba, unsigned int &comienzo, unsigned int &fin) const;

	/**
	 * Intenta combinar un cuba y hace la respectiva llamada a Contraer en caso de tener exito.
	 * Si el directorio se contrajo, se vuelve a llamar a IntentarCombinar recursivamente.
	 */
	void intentarCombinar(Cuba &cuba, unsigned long int posicionCuba);

	/* Agrega la cuba al almacenamiento de cubas, y agrega su direccion fisica en el directorio*/
	void agregarCuba(const Cuba &cuba);

	/* Modifica en el almacenamiento la cuba objetivo*/
	void modificarCuba(const Cuba &cuba);

	/* Elimina la cuba del almacenamiento de cubas, y del directorio*/
	void eliminarCuba(const unsigned long int posicion);

    /*
     * Aumenta el atributo profundidad en 1 y separa la cuba actual en dos Cubas. Aquellas
     * claves en las cuales el bit en la posicion profundidad valga 0, se quedan en la actual cuba,
     * el resto de las claves con sus respectivas referencias van a la nueva Cuba.
     */
	Cuba dividirCuba (Cuba &cubaLlena);
};


class ViolacionUnicidadException : public exception
{
	virtual const char* what() const throw()
  	{
    	return "Se intenta agregar al indice una clave que ya existe";
  	}
};


}

#endif /* INDEXADOHASHEXTENSIBLE_H_ */
